package com.hp.bon.sgw.wh.channel.handler;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.BuzProcessorManager;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.channel.SoapChannel;
import com.hp.bon.sgw.core.channel.handler.IAsynCallHandler;
import com.hp.bon.sgw.threads.TaskManager;
import com.hp.bon.sgw.wh.service.UserResourceConfigService;
import com.hp.bon.sgw.wh.utils.LogUtil;
import com.hp.bon.sgw.wh.utils.ResultCodeUtil;
import com.hp.ocs.diameter.DiameterUtils;
import com.hp.ocs.diameter.message.*;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.StringUtils;
import org.dom4j.*;
import org.simpleframework.http.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * @ClassName: QryBillHandle
 * @Description: 
 */
public class QryBillHandle implements IAsynCallHandler {
	private static final Logger _log = LoggerFactory.getLogger(QryBillHandle.class);

	private static final Object XmlMessage = null;
    private long startTime;
    private Request request;
	private PrintStream printStream;
	private JSONObject requestObject;
	private UserResourceConfigService userResourceConfigService ;
	public QryBillHandle(PrintStream printStream, JSONObject requestObject, Request request, long startTime) {
		this.printStream = printStream;
		this.requestObject = requestObject;
        this.startTime = startTime;
        this.request = request;
	}

	@Override
	public int getProtocolType() {
		return 0;
	}

	@Override
	public void onCallResult(CallParam callParam) {
        requestObject.put("webTimeLength",System.currentTimeMillis()-requestObject.getLongValue("jfStartTime"));
		XmlMessage ack = null;
		Object response = null;
		if (callParam.getResponse() != null) {
			_log.warn("getResponse():");
			response = callParam.getResponse();
		} else if (callParam.getRecvResponse() != null) {
			_log.warn("getRecvResponse():");
			response = callParam.getRecvResponse();
		}
		if (response == null) {
			int _retcodeTmp = callParam.getRetCode() == 3002 ? 11202 : callParam.getRetCode();
			ack = SoapChannel.createERRAnwser(callParam.getSessionId(), _retcodeTmp, callParam.getResultMsg());
			callParam.setServiceResultCode(_retcodeTmp);// 11203
		} else {
			if (response instanceof Message) {// CCA -> XML
				ack = BuzProcessorManager.getInstance().convertMessageToAck(callParam);
			} else if (response instanceof XmlMessage) {
				ack = (XmlMessage) response;
			} else {
				_log.warn("unknown response type:" + callParam.getResponse().getClass().getName());
			}
		}
		try {
			
			if (response instanceof Message) {
                _log.info("================response instanceof Message");
			}else if (response instanceof XmlMessage) {
                _log.info("================response instanceof XmlMessage");
            }
			String respXML = convertResponseString(response, callParam.getServiceResultCode());
            if("0".equals(respXML)){
                return;
            }
            byte[] resp = respXML.getBytes("UTF-8");
			ack.setLength(resp.length);
			ack.setMsgTime(System.currentTimeMillis());
			printStream.write(resp);
			printStream.close();
			callParam.setResponse(ack);
            //记录到日志
            LogUtil.saveLogToABM(request,respXML,startTime,requestObject);
			TaskManager.taskProcess(callParam);
            _log.info("http response(cfq) " + respXML);
		}  catch (Exception e) {
			_log.error("convertResponseString Exception,msg=" , e);
		}
	}
	

	
	
	/**
	 * 返回最终json格式数据
	 * @param answer
	 * @param serviceResultCode
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private String convertResponseString(Object answer, int serviceResultCode) {
		_log.info("QryBillHandle serviceResultCode = {}", serviceResultCode);
        _log.info("===========service_result_code===============" + serviceResultCode);
        String response = "";
		Map<String, Object> data = new HashMap<String, Object>();
        data.put("resultCode", "-1");
        data.put("resultMsg", "查询失败");
        if("1".equals(requestObject.get("qrybil_dcc_request"))){
            _log.info("=========去鉴权,走新DCC===");
            //user_resource_config.properties配置文件中增加配置项：
            //qrybil_dcc_request   0逻辑保持不变 1计费后端改调DCC接口
            String objValue = requestObject.getString("destinationAccount");
            String objAttr = requestObject.getString("destinationAttr");
            String billQueryType = requestObject.getString("billQueryType");
            String queryFlag = requestObject.getString("queryFlag");
            String feeQueryFlag = requestObject.getString("feeQueryFlag");
            String areaCode;
            if("2".equals(objAttr) || "6".equals(objAttr) || "027".equals(objValue.substring(0,3))){
                areaCode="027";
            }else{
                areaCode=objValue.substring(0,4);
            }
            Element serviceInformation = DocumentHelper.createElement("Service_Information");
            serviceInformation.addElement("Channel_Id").setText("0");
            serviceInformation.addElement("Area_Code").setText(areaCode);
            serviceInformation.addElement("DestinationAttr").setText(objAttr);
            serviceInformation.addElement("Destination_Id").setText(objValue);
            if("0".equals(billQueryType)){
                billQueryType="3";
            }
            serviceInformation.addElement("Destination_Id_Type").setText(billQueryType);
            serviceInformation.addElement("Query_Flag").setText(queryFlag);
            serviceInformation.addElement("Query_Item_Type").setText(feeQueryFlag);
            _log.info("=======serviceInformation.asXML():"+serviceInformation.asXML());
            XmlMessage req = new XmlMessage(true);
            String tmallHostId = "SGWHBCFQ@027.ctnbc-bon.net";
            String sessionId = DiameterUtils.makeSessionID(tmallHostId);
            req.setIp(requestObject.getString("IP"));
            req.setMsgTime(System.currentTimeMillis());
            req.setMID(sessionId);
            req.setUID(tmallHostId);
            req.setSCID("QryBillOwe.jf3@027.ctnbc-bon.net");
            req.setSIG(null);
            req.setREPTAG(0);
            req.setDATA(serviceInformation.asXML());
            req.setDataElement(serviceInformation);
            requestObject.put("jfStartTime", System.currentTimeMillis());
            // new CallParam对象
            QryBillHandle2 qryBillHandle = new QryBillHandle2(printStream, requestObject,request,startTime);
            CallParam callParam = new CallParam(req.getMID(), req.getSCID(), req, req.getUID(), qryBillHandle);
            BuzProcessorManager.getInstance().processRequest(callParam);
            return "0";
        }
		String HomeAreaCode="";
		String Result="";//DCC返回码
        //按照 Message 解析
        //AVP解析
        Message message=(Message) answer;
        Map<String, Object> map= getAvpsFromMessage(message);//DCC返回结果
        if(map != null) {
            HomeAreaCode =(String)map.get("Latn_Id");
            Result =(String)map.get("Result");
        }
		Gson gson = new Gson();
        _log.info("===========service_result_code===============" + Result);
        data.put("feeBillingCycle", new ArrayList<String>());
        if(HomeAreaCode == null || !"0".equals(Result)) {
			// 解析下游返回数据出错
			data.put("resultCode", Result);
			data.put("resultMsg", "查询失败");
		}else{
                // 调用webservice接口查询详单信息
                String url;
                String buildStringMes;
                String subStringTemp;
                url="1001".equals(HomeAreaCode) ? requestObject.getString("YECXURLWHjf3") : requestObject.getString("YECXURLHBjf3");
                buildStringMes = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://webservice.asiainfo.com/\">\r\n" +
                        "   <soapenv:Header/>\r\n" +
                        "   <soapenv:Body>\r\n" +
                        "      <web:qryBill>\r\n" +
                        "         <QryBillRequest>\r\n" +
                        "            <billQueryType>"+requestObject.getString("billQueryType")+"</billQueryType>\r\n" +
                        "            <destinationAccount>"+requestObject.getString("destinationAccount")+"</destinationAccount>\r\n" +
                        "            <destinationAttr>"+requestObject.getString("destinationAttr")+"</destinationAttr>\r\n" +
                        "            <queryFlag>"+requestObject.getString("queryFlag")+"</queryFlag>\r\n" +
                        "            <feeQueryFlag>"+requestObject.getString("feeQueryFlag")+"</feeQueryFlag>\r\n" +
                        "            <operAttrStruct>\r\n" +
                        "               <staffId>"+requestObject.getJSONObject("operAttrStruct").getString("staffId")+"</staffId>\r\n" +
                        "               <operOrgId>"+requestObject.getJSONObject("operAttrStruct").getString("operOrgId")+"</operOrgId>\r\n" +
                        "               <operTime>"+requestObject.getJSONObject("operAttrStruct").getString("operTime")+"</operTime>\r\n" +
                        "               <operPost>"+requestObject.getJSONObject("operAttrStruct").getString("operPost")+"</operPost>\r\n" +
                        "               <operServiceId>"+requestObject.getJSONObject("operAttrStruct").getString("operServiceId")+"</operServiceId>\r\n" +
                        "               <lanId>"+requestObject.getJSONObject("operAttrStruct").getString("lanId")+"</lanId>\r\n" +
                        "            </operAttrStruct>\r\n" +
                        "         </QryBillRequest>\r\n" +
                        "      </web:qryBill>\r\n" +
                        "   </soapenv:Body>\r\n" +
                        "</soapenv:Envelope>";
                subStringTemp="</ns1:qryBillResponse>";
                String resp="";

                try{
                    resp = sendWebService(buildStringMes,url);
                    _log.info("======解析XM start ====");
                    if(!StringUtils.isEmpty(resp) ){
                        resp = resp.substring(resp.indexOf("<QryBillResponse"), resp.indexOf(subStringTemp));
                        data.putAll(xml2map(resp));
                    }
                    //String 转 int
                    updateMap(data);
                }catch(Exception e){
                    _log.error("详单webservice调用异常：", e);
                }
                _log.info("======解析XM end ====");
		}
        String newCode = ResultCodeUtil.updateResultCode(data.get("resultCode"), userResourceConfigService.getValue("api_resultCode"), userResourceConfigService.getValue("api_defaultResult"));
        data.put("resultCode", newCode);
        response = gson.toJson(data);

		return response;
	}

	
	/**
	 * 处理CCA结果
	 * @param answer
	 * @return
	 */
	public Map<String, Object> getAvpsFromMessage(Message answer) {
		Map<String, Object> map = new HashMap<String, Object>();
		try {
			AVP avp = answer.find(Constants.DIAMETER_AVP_SERVICE_INFOMATION, Constants.DIAMETER_VENDOR_3GPP);
			AVP_Grouped avpServiceInfomation = new AVP_Grouped(avp);
			AVP[] avps = avpServiceInfomation.queryAVPs();
			for (AVP avpData : avps) {
				
				if (avpData.code() == 20631 && avpData.vendorID() == Constants.DIAMETER_VENDOR_CT) {
					map.put("Result", String.valueOf(new AVP_Unsigned32(avpData).queryValue()));
				}
				
				if (avpData.code() == 80615 && avpData.vendorID() == Constants.DIAMETER_VENDOR_CT) {
					map.put("HomeAreaCode", String.valueOf(new AVP_Unsigned32(avpData).queryValue()));
				}
				if (avpData.code() == 80411 && avpData.vendorID() == Constants.DIAMETER_VENDOR_CT) {
					map.put("Latn_Id", new AVP_UTF8String(avpData).queryValue());
				}
				
			}
		} catch (Exception e) {
			map = null;
			_log.error("getAvpsFromMessage Exception error,msg=" , e);
		}
		return map;
	}

    public Map xml2map(String xmlString) throws DocumentException {
        Document doc = DocumentHelper.parseText(xmlString);
        Element rootElement = doc.getRootElement();
        Map<String, Object> map = new HashMap<String, Object>();
        ele2map(map, rootElement);
        System.out.println(map);
        // 到此xml2map完成，下面的代码是将map转成了json用来观察我们的xml2map转换的是否ok
//        String string = JSONObject.fromObject(map).toString();
//        System.out.println(string);
        return map;
    }

    /***
     * 核心方法，里面有递归调用
     *
     * @param map
     * @param ele
     */
    public void ele2map(Map map, Element ele) {
        // 获得当前节点的子节点
        List<Element> elements = ele.elements();
        if (elements.size() == 0) {
            // 没有子节点说明当前节点是叶子节点，直接取值即可
            map.put(ele.getName(), ele.getText());
        } else if (elements.size() == 1) {
            // 只有一个子节点说明不用考虑list的情况，直接继续递归即可
            Map<String, Object> tempMap = new HashMap<String, Object>();
            ele2map(tempMap, elements.get(0));
            map.put(ele.getName(), tempMap);
        } else {
            // 多个子节点的话就得考虑list的情况了，比如多个子节点有节点名称相同的
            // 构造一个map用来去重
            Map<String, Object> tempMap = new HashMap<String, Object>();
            for (Element element : elements) {
                tempMap.put(element.getName(), null);
            }
            Set<String> keySet = tempMap.keySet();
            for (String string : keySet) {
                Namespace namespace = elements.get(0).getNamespace();
                List<Element> elements2 = ele.elements(new QName(string,namespace));
                // 如果同名的数目大于1则表示要构建list
                if (elements2.size() > 1) {
                    List<Map> list = new ArrayList<Map>();
                    for (Element element : elements2) {
                        Map<String, Object> tempMap1 = new HashMap<String, Object>();
                        ele2map(tempMap1, element);
                        list.add(tempMap1);
                    }
                    map.put(string, list);
                } else {
                    // 同名的数量不大于1则直接递归去
                    Map<String, Object> tempMap1 = new HashMap<String, Object>();
                    ele2map(tempMap1, elements2.get(0));
                    if(tempMap1.size() == 1 && tempMap1.keySet().contains(string)){
                        map.put(string,tempMap1.get(string));
                    }else{
                        map.put(string,tempMap1);
                    }

                }
            }
        }
    }
	
	/**
	 * 调用详单查询webservice接口
	 * @param param
	 * @return
	 */
	private String sendWebService(String param,String url) {
        requestObject.put("jfStartTime", System.currentTimeMillis());
        _log.info("===========WebServiceProcessor start===============");
		_log.info("===========WebServiceProcessor url===============" + url);
		_log.info("===========WebServiceProcessor param===============" + param);
		String resp = "";
		PostMethod post = new PostMethod(url);
		try {
			RequestEntity requestEntity = new StringRequestEntity(param, "text/xml", "UTF-8");
			post.setRequestEntity(requestEntity);
			HttpClient client = new HttpClient();
			client.getHttpConnectionManager().getParams().setConnectionTimeout(5 * 1000);
			client.getHttpConnectionManager().getParams().setSoTimeout(5000);
			client.executeMethod(post);
			resp = post.getResponseBodyAsString();
		} catch (UnsupportedEncodingException e) {
			_log.error("===========" + url + " call UnsupportedEncodingException===============", e);
		} catch (HttpException e) {
			_log.error("===========" + url + " call HttpException===============", e);
		} catch (IOException e) {
			_log.error("===========" + url + " call IOException===============", e);
		}
        requestObject.put("jfEndTime",System.currentTimeMillis());
        _log.info("===========WebServiceProcessor end===============");
		_log.info("===========WebServiceProcessor resp===============" + resp);

		return resp;
	}


    /**
     * 按照部分需求,部分String 需要改为 int
     */
    private void updateMap(Map<String,Object> map){
        _log.info("===========updateMap map==============="+new Gson().toJson(map));
        try {
            map.put("previousChange",Long.valueOf((String)map.get("previousChange")));
            map.put("acctId",Long.valueOf((String)map.get("acctId")));
            map.put("due",Long.valueOf((String)map.get("due")));
            map.put("derateDue",Long.valueOf((String)map.get("derateDue")));

            map.put("shouldCharge",Long.valueOf((String)map.get("shouldCharge")));
            map.put("validBalance",Long.valueOf((String)map.get("validBalance")));
            map.put("currentChange",Long.valueOf((String)map.get("currentChange")));
            List<Map<String,Object>> feeBillingCycle=new ArrayList<Map<String, Object>>();
            if(map.get("feeBillingCycle") instanceof HashMap){
                feeBillingCycle.add((Map<String,Object>)map.get("feeBillingCycle"));
            }else{
                feeBillingCycle=(List<Map<String,Object>>)map.get("feeBillingCycle");
            }
            if(feeBillingCycle != null){
                for (int i = 0; i < feeBillingCycle.size(); i++) {
                    Map<String,Object> fbcMap=feeBillingCycle.get(i);
                    List<Map<String,Object>> acctItemGroupList=new ArrayList<Map<String, Object>>();
                    if(fbcMap.get("acctItemGroupList") instanceof HashMap){
                        acctItemGroupList.add((Map<String,Object>)fbcMap.get("acctItemGroupList"));
                    }else{
                        acctItemGroupList=(List<Map<String,Object>>)fbcMap.get("acctItemGroupList");
                    }
                    if(acctItemGroupList != null){
                        for (int j = 0; j <acctItemGroupList.size(); j++) {
                            Map<String,Object> aigMap=acctItemGroupList.get(j);
                            List<Map<String, Object>> acctItemType = new ArrayList<Map<String, Object>>();
                            if(aigMap.get("acctItemType") instanceof HashMap){
                                acctItemType.add((Map<String,Object>)aigMap.get("acctItemType"));
                            }else{
                                acctItemType=(List<Map<String,Object>>)aigMap.get("acctItemType");
                            }
                            if(acctItemType != null){
                                for (int k = 0; k <acctItemType.size(); k++) {
                                    Map<String,Object> aitMap=acctItemType.get(k);
                                    aitMap.put("acctItemCharge", Long.valueOf((String) aitMap.get("acctItemCharge")));
//                                    acctItemType.set(k,aitMap);
                                }
                            }
                            aigMap.put("acctItemType",acctItemType);
//                            acctItemGroupList.set(j,aigMap);
                        }
                    }
                    fbcMap.put("billingCycleId",Long.valueOf((String)fbcMap.get("billingCycleId")));
                    fbcMap.put("acctItemGroupList",acctItemGroupList);
//                    feeBillingCycle.set(i,fbcMap);
                }
            }
            map.put("feeBillingCycle",feeBillingCycle);
        }catch (Exception e){
            _log.error("=========== Exception===============", e);
        }
    }

    public static void main(String[] args) {

    }
}
